package edu.hawaii.myisern.visualization.socialnet;
import java.awt.Color;
import java.awt.Dimension;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JSplitPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
// import edu.hawaii.myisern.model.MyIsernModel;

import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.filter.GraphDistanceFilter;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.FocusControl;
import prefuse.controls.NeighborHighlightControl;
import prefuse.controls.PanControl;
import prefuse.controls.WheelZoomControl;
import prefuse.controls.ZoomControl;
import prefuse.controls.ZoomToFitControl;
import prefuse.data.Graph;
import prefuse.data.Tuple;
import prefuse.data.event.TupleSetListener;
import prefuse.data.io.GraphMLReader;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.GraphLib;
import prefuse.util.PrefuseLib;
import prefuse.util.force.ForceSimulator;
import prefuse.util.ui.JForcePanel;
import prefuse.util.ui.JPrefuseApplet;
import prefuse.util.ui.JValueSlider;
import prefuse.util.ui.UILib;
import prefuse.visual.NodeItem;
import prefuse.visual.VisualGraph;
import prefuse.visual.VisualItem;

/**
 * This class implements the graph functionality for the MyISERN system.  It uses
 * the graphview example in the prefuse libraries as a base, and contains a few
 * additional changes.
 * 
 * @author Jeffrey Heer
 * @author Andrew Wong
 *
 */
public class SocialNetwork extends JPrefuseApplet {
  private static final String drawFinalString = "draw";
  
  private static final long serialVersionUID = 6799132719698940687L;

  private static final String graph = "graph";
  private static final String nodes = "graph.nodes";
  private static final String edges = "graph.edges";

  /**
   * Initializes the applet.
   */
  public void init() {
      UILib.setPlatformLookAndFeel();
      String filename = "/socialnet.xml";
      
      JComponent graphview = demo(filename, "name");
      this.getContentPane().add(graphview);
  }

  /**
   * Generates applet data for the java applet.  The function first looks in the
   * current folder for the filename; if not, it goes to "user.home" property
   * folder /.myisern for the xml file.  Note that the latter case will never
   * work on an actual applet since it accesses a server file on a client applet,
   * while the former will never work on a locally run applet.
   * 
   * @param datafile Filename of data to be read.
   * @param label Which label on the nodes to use.
   * @return JComponent applet data to display.
   */
  public static JComponent demo(String datafile, String label) {
      Graph g = null;
      if ( datafile == null ) {
          g = GraphLib.getGrid(15,15);
      } else {
          try {
              InputStream fin = SocialNetwork.class.getResourceAsStream(datafile);
              if (fin == null) {
                throw new NullPointerException();
              }
              g = new GraphMLReader().readGraph(fin);
          } catch ( Exception e ) {
              try {
                g = new GraphMLReader().readGraph(System.getProperty("user.home") 
                    + "/.myisern" + datafile);
              }
              catch ( Exception f ) {
                // we failed twice.  but only the first one is really useful
                e.printStackTrace();
                f.printStackTrace();
                System.exit(1);
              }
          }
      }

      return demo(g, label);
  }
  
  /**
   * Generates the JComponent data for the applet, based on the graph.
   * 
   * @param g Graph data to be visualized.
   * @param label Which label on the nodes to use.
   * @return JComponent applet data to display.
   */
  public static JComponent demo(Graph g, String label) {

      // create a new, empty visualization for our data
      final Visualization vis = new Visualization();
      VisualGraph vg = vis.addGraph(graph, g);
      vis.setValue(edges, null, VisualItem.INTERACTIVE, Boolean.FALSE);
      
      TupleSet focusGroup = vis.getGroup(Visualization.FOCUS_ITEMS); 
      focusGroup.addTupleSetListener(new TupleSetListener() {
          public void tupleSetChanged(TupleSet ts, Tuple[] add, Tuple[] rem) {
              for ( int i = 0; i < rem.length; ++i ) {
                  ((VisualItem)rem[i]).setFixed(false);
              }
              for ( int i = 0; i < add.length; ++i ) {
                  ((VisualItem)add[i]).setFixed(false);
                  ((VisualItem)add[i]).setFixed(true);
              }
              vis.run(drawFinalString);
          }
      });
      
      // set up the renderers
      LabelRenderer tr = new LabelRenderer(label);
      tr.setRoundedCorner(8, 8);
      vis.setRendererFactory(new DefaultRendererFactory(tr));
      
     
      
      // -- set up the actions ----------------------------------------------
      
      int maxhops = 4, hops = 4;
      final GraphDistanceFilter filter = new GraphDistanceFilter(graph, hops);

      ActionList draw = new ActionList();
      draw.add(filter);
      draw.add(new ColorAction(nodes, VisualItem.FILLCOLOR, ColorLib.rgb(200,200,255)));
      draw.add(new ColorAction(nodes, VisualItem.STROKECOLOR, 0));
      draw.add(new ColorAction(nodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0)));
      draw.add(new ColorAction(edges, VisualItem.FILLCOLOR, ColorLib.gray(200)));
      draw.add(new ColorAction(edges, VisualItem.STROKECOLOR, ColorLib.gray(200)));
      
      ColorAction fill = new ColorAction(nodes, 
              VisualItem.FILLCOLOR, ColorLib.rgb(200,200,255));
      fill.add("_fixed", ColorLib.rgb(255,100,100));
      fill.add("_highlight", ColorLib.rgb(255,200,125));
      
      ForceDirectedLayout fdl = new ForceDirectedLayout(graph);
      ForceSimulator fsim = fdl.getForceSimulator();
      fsim.getForces()[0].setParameter(0, -1.2f);
      
      ActionList animate = new ActionList(Activity.INFINITY);
      animate.add(fdl);
      animate.add(fill);
      animate.add(new RepaintAction());
      
      // finally, we register our ActionList with the Visualization.
      // we can later execute our Actions by invoking a method on our
      // Visualization, using the name we've chosen below.
      vis.putAction(drawFinalString, draw);
      vis.putAction("layout", animate);
      vis.runAfter(drawFinalString, "layout");
      
      
      // --------------------------------------------------------------------
      // STEP 4: set up a display to show the visualization
      
      Display display = new Display(vis);
      display.setSize(500,500);
      display.setForeground(Color.GRAY);
      display.setBackground(Color.WHITE);
      
      // main display controls
      display.addControlListener(new FocusControl(1));
      display.addControlListener(new DragControl());
      display.addControlListener(new PanControl());
      display.addControlListener(new ZoomControl());
      display.addControlListener(new WheelZoomControl());
      display.addControlListener(new ZoomToFitControl());
      display.addControlListener(new NeighborHighlightControl());
      
      display.setForeground(Color.GRAY);
      display.setBackground(Color.WHITE);
      
      // --------------------------------------------------------------------        
      // STEP 5: launching the visualization
      
      // create a panel for editing force values
      final JForcePanel fpanel = new JForcePanel(fsim);
      
      final JValueSlider slider = new JValueSlider("Distance", 0, maxhops, hops);
      slider.addChangeListener(new ChangeListener() {
          public void stateChanged(ChangeEvent e) {
              filter.setDistance(slider.getValue().intValue());
              vis.run(drawFinalString);
          }
      });
      slider.setBackground(Color.WHITE);
      slider.setPreferredSize(new Dimension(300,30));
      slider.setMaximumSize(new Dimension(300,30));
      
      // export to an image
      OutputStream foo = null;
      try {
        foo = new FileOutputStream("out.jpg");
      }
      catch (Exception e) {
//        return;
        e.printStackTrace();
      }
      display.saveImage(foo, "JPG", 1.0);
      
      Box cf = new Box(BoxLayout.Y_AXIS);
      cf.add(slider);
      cf.setBorder(BorderFactory.createTitledBorder("Connectivity Filter"));
      fpanel.add(cf);
      
      fpanel.add(Box.createVerticalGlue());
      
      // create a new JSplitPane to present the interface
      JSplitPane split = new JSplitPane();
      split.setLeftComponent(display);
      split.setRightComponent(fpanel);
      split.setOneTouchExpandable(true);
      split.setContinuousLayout(false);
      split.setDividerLocation(530);
      split.setDividerLocation(800);
      
      
      // position and fix the default focus node
      NodeItem focus = (NodeItem)vg.getNode(0);
      PrefuseLib.setX(focus, null, 400);
      PrefuseLib.setY(focus, null, 250);
      focusGroup.setTuple(focus);

      // now we run our action list and return
      return split;
  }
  
} // end of class GraphView
